#ifndef _DVMGPU_H_
#define _DVMGPU_H_

#include <cuda_runtime.h>
#include "dvmgpu_rts.h"

// Структура заголовка dvmgpu массива:
typedef struct {
    long dvmGpuStructAddr; // ссылка на внутреннюю структуру
    long steps[10]; // вместо 10 вписать количество измерений массива
    long offset; // смещение в штуках начала массива(присутствующей части) относительно базы
    long deviceBaseAddr; // база
} DvmGpuArrayHeader;

// Делает всю необходимую инициализацию. Должна быть вызвана до регистрации первого региона(данных или вычислений не важно).
long initdvm_gpu_();

// Регистрирует вычислительный регион
long crt_region_gpu_();

// Регистрирует регион данных
long crt_data_region_gpu_();

// Регистрирует DVM-массив в регионе, заполняет начальными данными заголовок GPU-массива (заголовок лежит на хосте)
// При вызове для региона данных - выполняет аллокацию и, если нужно, копирование
// При вызове для региона вычислений только регистрирует. Аллокации будут в startpl_gpu_ и loop_gpu_
void crtda_gpu_(DvmGpuRegionRef *InRegionGpu, long InDvmArray[], long OutDvmGpuArray[], void *InDeviceBaseAddr, long *InCopyinFlag, long *InCopyoutFlag);

// Обновление теневых граней
long strtsh_gpu_(DvmGpuComputeRegionRef *InComputeRegionRef, ShadowGroupRef *ShadowGroupRefPtr);
void donesh_gpu_(DvmGpuShadowRef *InDvmGpuShadowRefPtr);

// Создание параллельного DVM_GPU цикла
long crtpl_gpu_(DvmGpuComputeRegionRef *InComputeRegionGpu, LoopRef *InDvmLoop, long *InFirstInRegionFlag, long *InLastInRegionFlag);

// InAxisArray	?	массив i-й элемент которого содержит номер измерения параллельного цикла, соответствующего (i+1)-му измерению распределённого массива.
// InCoeffArray	?	массив, i-й элемент которого содержит коэффициент индексной переменной линейного правила выборки для (i+1)-го измерения распределённого массива.
// InConstArray	?	массив, i-й элемент которого содержит константу линейного правила  выборки для (i+1)-го измерения распределённого массива.
// Добавление ссылки на массив.
void add_array_reference_gpu_(DvmGpuLoopRef *InDvmGpuLoop, long InDvmGpuArray[], long InAxisArray[], long InCoeffArray[], long InConstArray[]);

// Опрос возможности выполнения на GPU цикла. Если возможно, то иницииализируются данные для управления ходом вычисления на GPU цикла и возвращается 1. Если невозможно, возвращает нуль и в случае, если есть на ГПУ данные, не выкачанные из соображений, что пока рано, то их надо выкачать сейчас, чтобы обеспечить счёт на ЦПУ всеми сделанными изменениями.
long startpl_gpu_(DvmGpuLoopRef *InDvmGpuLoop);

// Функция для нормализации состояния после того, как startpl_gpu_ вернуль нуль и цикл исполнился средствами хоста
void end_host_exec_gpu_(DvmGpuLoopRef *InDvmGpuLoop);

// Регистрация редукции в цикле
void insred_gpu_(DvmGpuLoopRef *InDvmGpuLoop, RedRef *InRedRefPtr, void *InDeviceArrayBaseAddr, void *InDeviceLocBaseAddr, AddrType *ArrayOffsetPtr, AddrType *LocOffsetPtr);

// Запрос на подготовку информации для запуска ядра для очередного кванта. Возвращает не ноль, если требуется выполнения ядра и ноль, если обработка окончена. Определяет: порцию dvm-цикла для обработки, порции всех зарегистрированных в регионе массивов, решетку блоков, размеры блоков, порции dvm-цикла для каждого блока. Также выполняет все необходимые аллокации DVM_GPU массивов и переписи данных туда и обратно(если необходимо). Последним своим вызовом(который возвращает нуль) завершает GPU-цикл, освобождает структуры управления этим циклом.
long loop_gpu_(DvmGpuLoopRef *InDvmGpuLoop, dim3 *OutBlocks, dim3 *InOutThreads, void *InDeviceBaseAddr, IndexType *InOutBlocksOffs);
long loop_ns_gpu_(DvmGpuLoopRef *InDvmGpuLoop, dim3 *OutBlocks, dim3 *InOutThreads, long InOutLowValues[], long InOutHighValues[]);

// Освобождение ненужных структур, подчистка всего, что осталось от вычислительного региона
void end_region_gpu_(DvmGpuComputeRegionRef *InOutComputeRegionGpu);

// Освобождение ненужных структур, подчистка всего, что осталось от региона данных
void end_data_region_gpu_(DvmGpuDataRegionRef *InOutDataRegionGpu);

void dvmh_get_actual_whole_(long InDvmArray[]);
void dvmh_get_actual_(long InOutDvmArray[], long InLower[], long InUpper[]);

// debug & tune
void set_fixed_block_(char *arg);
void set_fixed_block_int_(int *px, int *py, int *pz);

#endif
